#include "User.h"

User::User() {
    scene = 0;
    frameCount = 0;

    logfile.open("log.txt", ios_base::app | ios_base::out);

    logfile << "----------------------------------------------------------------\n";

    time_t rawtime = time(0);
    logfile << "heighmapRT started " << asctime(localtime(&rawtime));

    useColumnTracing = false;

    //TODO: Scene selection?
    scene = new KdTreeCellScene<TriCell>();

    viewRotateLeft = 0;
    viewRotateUp = 0; 
    lightRotateLeft = 0;
    lightRotateUp = 0;
    viewMove = 0;

    lightRotateSpeed = 0.1;
    viewRotateSpeed = 0.05;
    viewMoveSpeed = 16;

    mouseSensitivity = 0.005;

    mouseButton = -1;
}

void User::setViewsize(int x, int y) {
    viewer.viewsizeX = x;
    viewer.viewsizeY = y;

    logfile << "viewsize set to " << x << "x" << y << "\n";
}

//macro for readability
#define PROCESS_OP(op_name) \
    if (!strcmp(op, #op_name)) { \
        op_ ## op_name ## (configfile); \
        continue; \
    }

void User::useConfigFile(const char *filename) {
    ifstream configfile(filename);
    char op[255];

    
    while (true) {
        configfile >> op;
        if (!configfile.good()) break;

        PROCESS_OP(fov);
        PROCESS_OP(viewer_pos);
        PROCESS_OP(viewer_dir);
        PROCESS_OP(viewsize);
        PROCESS_OP(heightmap);
        PROCESS_OP(userLight);
        PROCESS_OP(columnTracing);
        PROCESS_OP(viewMoveSpeed);
        PROCESS_OP(mouseSensitivity);

        logfile << "Unrecognized op '" << op << "'\n";
    }

    configfile.close();
}

void User::op_fov(ifstream& configfile) {
    float fov;
    configfile >> fov;

    viewer.fov = radf(fov);

    logfile << "fov set to " << fov << " degrees\n";
}

void User::op_viewer_pos(ifstream& configfile) {
    configfile >> viewer.pos;

    logfile << "initial viewer position set to " << viewer.pos << "\n";
}

void User::op_viewer_dir(ifstream& configfile) {
    configfile >> viewer.dir;
    viewer.dir.normalize();

    logfile << "initial viewer direction set to " << viewer.dir << "\n";
}

void User::op_viewsize(ifstream& configfile) {
    configfile >> viewer.viewsizeX >> viewer.viewsizeY;
}

void User::op_heightmap(ifstream& configfile) {
    char filename[255];
    float scale;
    configfile >> filename >> scale;
    FIBITMAP* bitmapRaw = FreeImage_Load(FIF_PNG, filename);
    Heightmap heightmap;
    heightmap.setData(bitmapRaw, scale);
    scene->assignHeightmap(heightmap);

    logfile << "heightmap set to '" << filename << "' with scale " << scale << "\n";
}

void User::op_userLight(ifstream& configfile) {
    Vector3f dir;
    Color3f color;
    configfile >> dir >> color;
    dir.normalize();
    DirectionalLight* light = new DirectionalLight(dir, color);
    scene->assignUserLight(light);
    logfile << "userLight set.\n";
}

void User::op_columnTracing(ifstream& configfile) {
    useColumnTracing = true;
    logfile << "Using column tracing.\n";
}

void User::op_viewMoveSpeed(ifstream& configfile) {
    configfile >> viewMoveSpeed;

    logfile << "View move speed set to " << viewMoveSpeed << ".\n";
}

void User::op_mouseSensitivity(ifstream& configfile) {
    configfile >> mouseSensitivity;

    logfile << "Mouse sensitivity set to " << mouseSensitivity << ".\n";
}

void User::ready() {
    scene->ready();
    readyClock = clock();
    frameClock = clock();
}

void User::render() {
    clock_t elapsedClock = clock() - frameClock;
    float elapsedTime = float(elapsedClock) / CLOCKS_PER_SEC;
    frameClock = clock();

    if (elapsedTime > 1) elapsedTime = 1;

    viewer.pos += viewer.dir * viewMove * elapsedTime;
    viewer.dir.rotateLeft(viewRotateLeft * elapsedTime);
    viewer.dir.rotateUp(viewRotateUp * elapsedTime);
    scene->modifyUserLight(lightRotateLeft * elapsedTime, lightRotateUp * elapsedTime);

    if (mouseButton == GLUT_LEFT_BUTTON) {
        viewer.dir.rotateLeft(-diffMouseX * mouseSensitivity);
        viewer.dir.rotateUp(-diffMouseY * mouseSensitivity);
    } else if (mouseButton == GLUT_RIGHT_BUTTON) {
        scene->modifyUserLight(-diffMouseX * mouseSensitivity, -diffMouseY * mouseSensitivity);
    }

    diffMouseX = 0;
    diffMouseY = 0;

    glClear(GL_COLOR_BUFFER_BIT);
   
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    if (useColumnTracing) monoSampleSlopeColumn(*scene, viewer);
    else monoSample(*scene, viewer);

    glFlush();
    glutSwapBuffers();
    frameCount++;
}

void User::reshape(int w, int h) {
    setViewsize(w, h);

	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0, w, 0, h);
}

void User::idle() {
    glutPostRedisplay();
}

void User::processNormalKeys(unsigned char key, int x, int y) {
    if (key == 'x') quit();

    if (key == 'a') viewRotateLeft = viewRotateSpeed;
    if (key == 'd') viewRotateLeft = -viewRotateSpeed;
    if (key == 'w') viewRotateUp = viewRotateSpeed;
    if (key == 's') viewRotateUp = -viewRotateSpeed;

    if (key == 'e') viewMove = viewMoveSpeed;
    if (key == 'q') viewMove = -viewMoveSpeed;

    if (key == 'j') lightRotateLeft = lightRotateSpeed;
    if (key == 'l') lightRotateLeft = -lightRotateSpeed;
    if (key == 'i') lightRotateUp = lightRotateSpeed;
    if (key == 'k') lightRotateUp = -lightRotateSpeed;

    if (key == 'c') {
        useColumnTracing = !useColumnTracing;
        if (useColumnTracing) printf("Column tracing activated.\n");
        else printf("Column tracing deactivated.\n");
    }
}

void User::keyboardUp(unsigned char key, int x, int y) {
    if (key == 'a') viewRotateLeft = 0;
    if (key == 'd') viewRotateLeft = 0;
    if (key == 'w') viewRotateUp = 0;
    if (key == 's') viewRotateUp = 0;

    if (key == 'e') viewMove = 0;
    if (key == 'q') viewMove = 0;
    
    if (key == 'j') lightRotateLeft = 0;
    if (key == 'l') lightRotateLeft = 0;
    if (key == 'i') lightRotateUp = 0;
    if (key == 'k') lightRotateUp = 0;
}

void User::mouseState(int button, int state, int x, int y) {
    if (state == GLUT_DOWN) {
        mouseButton = button;
        lastMouseX = x;
        lastMouseY = y;
    } else if (state == GLUT_UP) {
        mouseButton = -1;
    }
}

void User::mouseMotion(int x, int y) {
    if (mouseButton >= 0) {
        diffMouseX = x - lastMouseX;
        diffMouseY = y - lastMouseY;
    }
    lastMouseX = x;
    lastMouseY = y;
}

void User::quit() {
    clock_t renderClock = clock() - readyClock;
    float renderTime = float(renderClock) / CLOCKS_PER_SEC;
    FreeImage_DeInitialise();
    logfile << "Total frames rendered: " << frameCount << "\n";
    logfile << "Rendering time: " << renderTime << "\n";
    logfile << "Frames per second: " << frameCount / renderTime << "\n";
    logfile.close();
    exit(0);
}
